/* 
 * sbcontainer core 1.0
 */
(function ($) {
    // private variables 
    var instances = [],            // instance array (used by $.sbcontainer.reference/create/focused)
        focused_instance = -1,    // the index in the instance array of the currently focused instance
        plugins = {},            // list of included plugins
        prepared_move = {},        // for the move plugin
        is_ie6 = false;

    // jQuery plugin wrapper (thanks to jquery UI widget function)
    $.fn.sbcontainer = function (settings) {
        var isMethodCall = (typeof settings == 'string'), // is this a method call like $().sb-container("open_node")
            args = Array.prototype.slice.call(arguments, 1), 
            returnValue = this;

        // extend settings and allow for multiple hashes and metadata
        if (!isMethodCall && $.meta) { args.push($.metadata.get(this).sbcontainer); }
        settings = !isMethodCall && args.length ? $.extend.apply(null, [true, settings].concat(args)) : settings;
        // block calls to "private" methods
        if (isMethodCall && settings.substring(0, 1) == '_') { return returnValue; }

        // if a method call execute the method on all selected instances
        if (isMethodCall) {
            this.each(function() {
                var instance = instances[$.data(this, "sb-container-instance-id")],
                    methodValue = (instance && $.isFunction(instance[settings])) ? instance[settings].apply(instance, args) : instance;
                    if (typeof methodValue !== "undefined" && (settings.indexOf("is_") === 0 || (methodValue !== true && methodValue !== false))) { returnValue = methodValue; return false; }
            });
        }
        else {
            this.each(function() {
                var instance_id = $.data(this, "sb-container-instance-id"),
                    s = false;
                // if an instance already exists, destroy it first
                if (typeof instance_id !== "undefined" && instances[instance_id]) { instances[instance_id].destroy(); }
                // push a new empty object to the instances array
                instance_id = parseInt(instances.push({}),10) - 1;
                // store the sbcontainer instance id to the container element
                $.data(this, "sb-container-instance-id", instance_id);
                // clean up all plugins
                if (!settings) { settings = {}; }
                settings.plugins = $.isArray(settings.plugins) ? settings.plugins : $.sbcontainer.defaults.plugins;
                if ($.inArray("core", settings.plugins) === -1) { settings.plugins.unshift("core"); }
                
                // only unique plugins (NOT WORKING)
                // settings.plugins = settings.plugins.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",");

                // extend defaults with passed data
                s = $.extend(true, {}, $.sbcontainer.defaults, settings);
                s.plugins = settings.plugins;
                $.each(plugins, function (i, val) { if($.inArray(i, s.plugins) === -1) { s[i] = null; delete s[i]; } });
                // push the new object to the instances array (at the same time set the default classes to the container) and init
                instances[instance_id] = new $.sbcontainer._instance(instance_id, $(this).addClass("sb-container sb-container-" + instance_id), s); 
                // init all activated plugins for this instance
                $.each(instances[instance_id]._get_settings().plugins, function (i, val) { instances[instance_id].data[val] = {}; });
                $.each(instances[instance_id]._get_settings().plugins, function (i, val) { if(plugins[val]) { plugins[val].__init.apply(instances[instance_id]); } });
                // initialize the instance
                instances[instance_id].init();
            });
        }
        // return the jquery selection (or if it was a method call that returned a value - the returned value)
        return returnValue;
    };
    // object to store exposed functions and objects
    $.sbcontainer = {
        defaults : {
            plugins : []
        },
        _focused : function () { return instances[focused_instance] || null; },
        _reference : function (needle) { 
            // get by instance id
            if(instances[needle]) { return instances[needle]; }
            // get by DOM (if still no luck - return null
            var o = $(needle); 
            if(!o.length && typeof needle === "string") { o = $("#" + needle); }
            if(!o.length) { return null; }
            return instances[o.closest(".sb-container").data("sb-container-instance-id")] || null; 
        },
        _instance : function (index, container, settings) { 
            // for plugins to store data in
            this.data = { core : {} };
            this.get_settings    = function () { return $.extend(true, {}, settings); };
            this._get_settings    = function () { return settings; };
            this.get_index        = function () { return index; };
            this.get_container    = function () { return container; };
            this._set_settings    = function (s) { 
                settings = $.extend(true, {}, settings, s);
            };
        },
        _fn : { },
        plugin : function (pname, pdata) {
            pdata = $.extend({}, {
                __init        : $.noop, 
                __destroy    : $.noop,
                _fn            : {},
                defaults    : false
            }, pdata);
            plugins[pname] = pdata;

            $.sbcontainer.defaults[pname] = pdata.defaults;
            $.each(pdata._fn, function (i, val) {
                val.plugin        = pname;
                val.old            = $.sbcontainer._fn[i];
                $.sbcontainer._fn[i] = function () {
                    var rslt,
                        func = val,
                        args = Array.prototype.slice.call(arguments),
                        evnt = new $.Event("before.sbcontainer"),
                        rlbk = false;

                    // Check if function belongs to the included plugins of this instance
                    do {
                        if(func && func.plugin && $.inArray(func.plugin, this._get_settings().plugins) !== -1) { break; }
                        func = func.old;
                    } while(func);
                    if(!func) { return; }

                    // a chance to stop execution (or change arguments): 
                    // * just bind to sbcontainer.before
                    // * check the additional data object (func property)
                    // * call event.stopImmediatePropagation()
                    // * return false (or an array of arguments)
                    rslt = this.get_container().triggerHandler(evnt, { "func" : i, "inst" : this, "args" : args });
                    if(rslt === false) { return; }
                    if(typeof rslt !== "undefined") { args = rslt; }

                    // context and function to trigger events, then finally call the function
                    if(i.indexOf("_") === 0) {
                        rslt = func.apply(this, args);
                    }
                    else {
                        rslt = func.apply(
                            $.extend({}, this, { 
                                __callback : function (data) { 
                                    this.get_container().triggerHandler( i + '.sbcontainer', { "inst" : this, "args" : args, "rslt" : data, "rlbk" : rlbk });
                                },
                                __rollback : function () { 
                                    rlbk = this.get_rollback();
                                    return rlbk;
                                },
                                __call_old : function (replace_arguments) {
                                    return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) );
                                }
                            }), args);
                    }

                    // return the result
                    return rslt;
                };
                $.sbcontainer._fn[i].old = val.old;
                $.sbcontainer._fn[i].plugin = pname;
            });
        },
        rollback : function (rb) {
            if(rb) {
                if(!$.isArray(rb)) { rb = [ rb ]; }
                $.each(rb, function (i, val) {
                    instances[val.i].set_rollback(val.h, val.d);
                });
            }
        }
    };
    // set the prototype for all instances
    $.sbcontainer._fn = $.sbcontainer._instance.prototype = {};

    // core functions (open, close, create, update, delete)
    $.sbcontainer.plugin("core", {
        __init : function () {
            this.load_widget();
        },
        defaults : {
            strings : {
                loading     : "Loading ...",
                new_widget  : "New widget"
            },
            debug : false
        },
        _fn : { 
            init : function () {
                this.get_container()
                    .bind('mousedown.sbcontainer', $.proxy(function () { 
                        this.set_focus();
                    }, this))
                    .bind('mouseover.sbcontainer', $.proxy(function () { 
                        this.get_container().addClass('sb-container-hover');
                    }, this))
                    .bind('mouseout.sbcontainer', $.proxy(function () { 
                        this.get_container().removeClass('sb-container-hover');
                    }, this));
                this.refresh();
            },
            destroy : function () { 
                var i,
                    n = this.get_index(),
                    s = this._get_settings(),
                    _this = this;

                $.each(s.plugins, function (i, val) {
                    try { plugins[val].__destroy.apply(_this); } catch(err) { }
                });
                this.__callback();
                // set focus to another instance if this one is focused
                if(this.is_focused()) { 
                    for(i in instances) { 
                        if(instances.hasOwnProperty(i) && i != n) { 
                            instances[i].set_focus(); 
                            break; 
                        } 
                    }
                }
                // if no other instance found
                if(n === focused_instance) { focused_instance = -1; }
                // remove all traces of sb-container in the DOM (only the ones set using sb-container*) and cleans all events
                this.get_container()
                    .unbind(".sbcontainer")
                    .undelegate(".sbcontainer")
                    .removeData("sb-container-instance-id")
                    .find("[class^='sb-container']")
                        .andSelf()
                        .attr("class", function () { return this.className.replace(/sb-container[^ ]*|$/ig,''); });
                // remove the actual data
                instances[n] = null;
                delete instances[n];
            },
            // rollback
            get_rollback : function () { 
                this.__callback();
                return { i : this.get_index(), h : this.get_container().clone(true), d : this.data }; 
            },
            set_rollback : function (html, data) {
                this.get_container().empty().replaceWith(html);
                this.data = data;
                this.__callback();
            },
            // Dummy function to fire after the first load (so that there is a jstree.loaded event)
            loaded : function () { 
                this.__callback(); 
            },
            // deal with focus
            set_focus : function () { 
                var f = $.sbcontainer._focused();
                if(f && f !== this) {
                    f.get_container().removeClass("sb-container-focused"); 
                }
                if(f !== this) {
                    this.get_container().addClass("sb-container-focused"); 
                    focused_instance = this.get_index(); 
                }
                this.__callback();
            },
            is_focused : function () { 
                return focused_instance == this.get_index(); 
            },
            // traverse
            _get_widget : function (obj) { 
                var $obj = $(obj, this.get_container()); 
                if($obj.is(".sb-widget") || obj == -1) { return -1; } 
                $obj = $obj.closest("div.sb-widget", this.get_container()); 
                return $obj.length ? $obj : false; 
            },
            _load_widget : function (obj) {
                var sb_widget = $(obj);
                
                //wrap widget content
                var sb_widget_wrapper = $('<div class="sb-widget-wrapper"></div>');
                var sb_widget_content = $('<div class="sb-widget-content"></div>');
                sb_widget_wrapper.wrapInner(sb_widget_content);
                sb_widget.wrapInner(sb_widget_wrapper);
                
                var matchs = sb_widget.attr('class').match(/page\-widget\-(\d+)/);
                if (matchs && matchs.length == 2)
                {
                    var page_widget_id = 'page_widget_';
                    page_widget_id += matchs[1];
                    sb_widget.attr('id', page_widget_id);
                }
                //fix parent overflow
                if (this.get_container().hasClass('has-overflow'))
                    sb_widget.css({'cssText' : 'padding-left: 26px !important'});
                
                //toolbox
                var sb_widget_toolbox = '<div class="sb-widget-toolbox">'
                                            +'<a title="Move element up" class="sb-widget-toolbox-move-up" href="javascript:;"></a>'
                                            +'<a title="Move element down" class="sb-widget-toolbox-move-down" href="javascript:;"></a>'
                                            +'<a title="Edit element" class="sb-widget-toolbox-edit" href="javascript:;"></a>'
                                            +'<a title="Delete element" class="sb-widget-toolbox-remove" href="javascript:;"></a>'
                                            +'<a title="Add new element" class="sb-widget-toolbox-add" href="javascript:;"></a>'
                                            +'<a title="Change layout" class="sb-widget-toolbox-layout" href="javascript:;"></a>'
                                            +'<a title="Duplicate" class="sb-widget-toolbox-duplicate" href="javascript:;"></a>'
                                        +'</div>';
                sb_widget_toolbox = $(sb_widget_toolbox);
                sb_widget.prepend(sb_widget_toolbox);
                
                //overlay
                var sb_widget_overlay = '<div class="sb-widget-overlay">'
                                            +'<div class="sb-widget-overlay-bg"></div>'
                                            +'<div class="sb-widget-drag-handle">'
                                                +'<div></div>'
                                            +'</div>'
                                        +'</div>';
                sb_widget.prepend($(sb_widget_overlay));
                
                return sb_widget;
            },
            load_widget : function () {
                var widgets = $('.sb-widget', this.get_container());
                $.each(widgets, $.proxy(function(index, obj){
                    this._load_widget(obj);
                }, this));
                this.__callback();
                this.loaded();
            },
            create_widget : function (ref) {
                this.__callback({'obj' : ref});
            },
            change_layout_widget : function (ref) {
                this.__callback({'obj' : ref});
            },
            duplicate_widget : function (ref) {
                //var new_widget = ref.clone();
                //ref.after(new_widget);
                //this.__callback({'obj' : new_widget, 'ref' : ref});
                this.__callback({'ref' : ref});
            },
            add_widget : function (obj, ref, position) {
                switch (true)
                {
                    case (ref != null && position == 'before'):
                        $(ref).before(obj);
                        break;
                    case (ref != null && position == 'after'):
                        $(ref).after(obj);
                        break;
                    case (position == 'first'):
                        this.get_container().prepend(obj);
                        break;
                    case (ref == null || position == 'last'):
                        this.get_container().append(obj);
                        break;
                }
                this._load_widget(obj);
                this.__callback({ "obj" : obj, 'ref' : ref, 'position' : position});
                this.order_changed();
                this.refresh();
                return obj;
            },
            edit_widget : function (obj) {
                this.debug('edit_widget', obj);
                this.__callback({'obj': obj});
                return obj;
            },
            update_widget : function (obj, new_obj) {
                this.debug('update_widget', obj, new_obj);
                new_obj = this._load_widget(new_obj);
                obj.replaceWith(new_obj);
                this.__callback({'obj' : obj, 'new_obj' : new_obj});
                this.refresh();
                return new_obj;
            },
            receive_widget : function (obj) {
                this.debug('receive_widget', obj);
                this.__callback({'obj': obj});
                this.refresh();
                return obj;
            },
            remove_widget : function (obj) {
                this.__callback({ "obj" : obj });
                this.refresh();
                return obj;
            },
            before_delete_widget : function (obj) {
                this.data.core.allow_delete = true;
                this.__callback({ "obj" : obj });
            },
            delete_widget : function (obj) {
                obj = $(obj);
                if (!obj.length) return false;
                this.before_delete_widget(obj);
                if (this.data.core.allow_delete == false)
                    return false;
                
                this.__rollback();
                obj = obj.remove();
                this.__callback({ "obj" : obj });
                this.order_changed();
                this.refresh();
                return obj;
            },
            move_widget : function (obj, ref, position) {
                switch (position)
                {
                    case 'up':
                        $(ref).before(obj);
                        break;
                    case 'down':
                        $(ref).after(obj);
                        break;
                }
                this.__callback({ "obj" : obj, 'ref' : ref, 'position' : position});
                this.order_changed();
            },
            refresh : function () {
                //sender empty
                if (this.get_widgets().length <= 0)
                {
                    this.get_container()
                        .addClass('sb-container-empty')
                        .append('<a class="sb-container-btn sb-container-add-btn" href="#"><span></span>Add new element</a>');
                    if (this.get_container().hasClass('disable-change-layout') == false)
                    {
                        this.get_container().append('<a class="sb-container-btn sb-container-layout-btn" href="#"><span></span>Layout</a>');
                    }
                }
                else
                {
                    //remove empty class
                    if (this.get_container().hasClass('sb-container-empty'))
                    {
                        var buttonAdd = $('.sb-container-add-btn', this.get_container());
                        if (buttonAdd)
                            buttonAdd.remove();
                        this.get_container().removeClass('sb-container-empty');
                    }
                }
            },
            get_container_id : function () {return this.get_container().attr('id');},
            get_page_widget_id : function (obj) {
                var $obj = $(obj);
                var matchs = $obj.attr('id').match(/page_widget_(\d+)/);
                if (matchs && matchs.length == 2)
                    return parseInt(matchs[1]);
                return 0;
            },
            get_widget_instance_id : function (obj) {
                var $obj = $(obj);
                var matchs = $obj.attr('class').match(/widget_instance_(\d+)/);
                if (matchs && matchs.length == 2)
                    return parseInt(matchs[1]);
                return 0;
            },
            get_widget_layout_id : function (obj) {
                var $obj = $(obj);
                var matchs = $obj.attr('class').match(/widget_layout_(\d+)/);
                if (matchs && matchs.length == 2)
                    return parseInt(matchs[1]);
                return 0;
            },
            get_widget_id : function (obj) {
                var $obj = $(obj);
                var matchs = $obj.attr('class').match(/widget_(\d+)/);
                if (matchs && matchs.length == 2)
                    return parseInt(matchs[1]);
                return 0;
            },
            get_widgets : function () {return $('.sb-widget', this.get_container());},
            order_changed : function () {
                var query = this.get_container().sortable('serialize');
                var items = this.get_container().sortable('toArray');
                this.__callback({'query' : query, 'items' : items});
            },
            debug : function () {
                if (window.console && this.get_settings().core.debug)
                    window.console.log.apply(this, arguments);
            }
        }
    });
})(jQuery);

(function ($) {
    $.sbcontainer.plugin("ui", {
        __init : function () {
            this.get_container()
                .delegate('.sb-widget', 'mouseover', function(){
                    // try to fix the wrapper height if it's content is floating or position absolute
                    if ($('.sb-widget-wrapper', this).height() <= 0){
                        maxHeight = 0;
                        $('.sb-widget-wrapper', this).find('.sb-widget-content').children().each(function(){
                            if (maxHeight < $(this).height())
                                maxHeight = $(this).height();
                        });
                        if (maxHeight == 0) maxHeight = 24; //try 2 times of a character
                        $('.sb-widget-wrapper', this).height(maxHeight);
                        if (window.console) console.log(maxHeight);
                    }
                    $('.sb-widget-overlay', this).css({
                        'width' : $('.sb-widget-wrapper', this).width(),
                        'height' : $('.sb-widget-wrapper', this).height()
                    });
                    $('.sb-widget-overlay .sb-widget-drag-handle', this).show();
                    $(this).addClass('sb-widget-hover');
                })
                .delegate('.sb-widget', 'mouseout', function(){
                    $('.sb-widget-overlay .sb-widget-drag-handle', this).hide();
                    $(this).removeClass('sb-widget-hover');
                })
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-remove', 'click', $.proxy(function(event){
                    var obj = $(event.currentTarget).closest('.sb-widget');
                    this.delete_widget(obj);
                    return false;
                }, this))
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-move-up', 'click', $.proxy(function(event){
                    var obj = $(event.currentTarget).closest('.sb-widget');
                    var ref = obj.prev();
                    if (!ref.length) return false;
                    this.move_widget(obj, ref, 'up');
                    
                    //fix bug show toolbox, overlay after moved
                    if (obj.hasClass('sb-widget-hover'))
                        obj.trigger('mouseout');
                    
                    return false;
                }, this))
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-move-down', 'click', $.proxy(function(event){
                    var obj = $(event.currentTarget).closest('.sb-widget');
                    var ref = obj.next();
                    if (!ref.length) return false;
                    this.move_widget(obj, ref, 'down');
                    
                    //fix bug show toolbox, overlay after moved
                    if (obj.hasClass('sb-widget-hover'))
                        obj.trigger('mouseout');
                    
                    return false;
                }, this))
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-add', 'click', $.proxy(function(event){
                    var ref = $(event.currentTarget).closest('.sb-widget');
                    
                    this.create_widget(ref);
                    
                    return false;
                }, this))
                .delegate('.sb-container-layout-btn, .sb-widget-toolbox .sb-widget-toolbox-layout', 'click', $.proxy(function(event){
                    var ref = $(event.currentTarget).closest('.sb-widget');
                    
                    this.change_layout_widget(ref);
                    
                    return false;
                }, this))
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-duplicate', 'click', $.proxy(function(event){
                    var ref = $(event.currentTarget).closest('.sb-widget');
                    
                    this.duplicate_widget(ref);
                    
                    return false;
                }, this))
                .delegate('.sb-widget-toolbox .sb-widget-toolbox-edit', 'click', $.proxy(function(event){
                    var obj = $(event.currentTarget).closest('.sb-widget');
                    
                    this.edit_widget(obj);
                    
                    return false;
                }, this))
                .delegate('.sb-container-add-btn', 'click', $.proxy(function(event){
                    var ref = $(event.currentTarget).closest('.sb-widget');
                    
                    this.create_widget(ref);
                    
                    return false;
                }, this))
                .delegate('.sb-widget-overlay-bg', 'click', $.proxy(function(event){
                    var obj = $(event.currentTarget).closest('.sb-widget');
                    
                    this.edit_widget(obj);
                    
                    return false;
                }, this))
                .sortable({
                    'items' : '.sb-widget',
                    'handle' : '.sb-widget-drag-handle',
                    'placeholder' : 'sb-sortable-placeholder',
                    'connectWith' : '.sb-container',
                    update : $.proxy(this._update_container, this),
                    remove : $.proxy(this._remove_widget, this),
                    receive : $.proxy(this._receive_widget, this),
                    over : $.proxy(this._over_widget, this),
                    out : $.proxy(this._out_widget, this),
                    start : $.proxy(this._start_sort, this)
                })
                .disableSelection();
        },
        _fn : {
            _update_container : function(event, ui) {
                //fix bug show toolbox, overlay after moved
                if (ui.item.hasClass('sb-widget-hover'))
                    ui.item.trigger('mouseout');
                
                this.order_changed();
            },
            _remove_widget : function(event, ui) {
                return this.remove_widget(ui.item);
            },
            _receive_widget : function(event, ui) {
                //fix bug show toolbox, overlay after moved
                if (ui.item.hasClass('sb-widget-hover'))
                    ui.item.trigger('mouseout');
                    
                //convert item match with container tag
                //container is ul, dl
                var containerTagName = this.get_container().get(0).tagName.toLowerCase();
                var itemTagName = ui.item.get(0).tagName.toLowerCase();
                var containerIsList = $.inArray(containerTagName, ['ul', 'dl']) > -1;
                var itemIsItemList = $.inArray(itemTagName, ['li', 'dt', 'dd']) > -1;
                if (containerIsList && !itemIsItemList)
                {
                    //replace item tag by li,dd,dl
                    var newItem = $('<li></li>');
                    if (containerTagName == 'dl')
                        var newItem = $('<dt></dt>');
                    
                    newItem.attr({
                            'id': ui.item.attr('id'), 
                            'class': ui.item.attr('class')
                        })
                        .html(ui.item.html());
                    ui.item.replaceWith(newItem);
                }
                else if (!containerIsList && itemIsItemList)
                {
                    //replace item tag by div
                    var newItem = $('<div></div>')
                        .attr({
                            'id': ui.item.attr('id'), 
                            'class': ui.item.attr('class')
                        })
                        .html(ui.item.html());
                    ui.item.replaceWith(newItem);
                }
                
                return this.receive_widget(ui.item);
            },
            _over_widget : function(event, ui) {
                if (this.get_container().hasClass('sb-container-empty'))
                {
                    var buttonAdd = $('.sb-container-btn', this.get_container());
                    if (buttonAdd)
                        buttonAdd.hide();
                }
            },
            _out_widget : function(event, ui) {
                if (this.get_container().hasClass('sb-container-empty'))
                {
                    var buttonAdd = $('.sb-container-btn', this.get_container());
                    if (buttonAdd)
                        buttonAdd.show();
                }
            },
            _start_sort : function(event, ui) {
                var height = $('.sb-widget-wrapper', ui.item).height();
                if (height > 100)
                    height = 100;
                $('.sb-sortable-placeholder').css({
//                    'width' : $('.sb-widget-wrapper', ui.item).width(),
                    'height' : height
                });
            }
        }
    });
    $.sbcontainer.defaults.plugins.push("ui");
})(jQuery);